#include "stdafx.h"

CComPtr<ICLRRuntimeHost> gRuntimeHost = nullptr;
DWORD gAppDomain = 0;


struct StartupInfo
{
	std::wstring assemblyPath;
	std::wstring typeName;
	std::wstring functionName;
	std::wstring parameters;

	StartupInfo(std::wstring path, std::wstring type, std::wstring function, std::wstring params) :
		assemblyPath(path),
		typeName(type),
		functionName(function),
		parameters(params)
	{
	}
};


StartupInfo ExtractStartupInfo()
{
	Registry reg(HKEY_CURRENT_USER);

	if(reg.KeyExists(L"Software\\Gype\\") == false)
		throw 0;

	reg.OpenPath(L"Software\\Gype\\");
	
	if(reg.AttributeExists(L"InstallDir") == false)
		throw 1;

	auto dir = reg.GetAttribute<std::wstring>(L"InstallDir");

	dir += L"\\CLRLoadTest.dll";

	return StartupInfo(dir, L"CLRLoadTest.TestClass", L"Main", L"");
}

void LoadCLR()
{
	CComPtr<ICLRMetaHost> metaHost = nullptr;
	CComPtr<ICLRRuntimeInfo> runtimeInfo = nullptr;

	const LPCWSTR dotNetVersion = L"v4.0.30319";

	auto result = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<void**>(&metaHost));
	if(FAILED(result))
		throw result;

	result = metaHost->GetRuntime(dotNetVersion, IID_ICLRRuntimeInfo, reinterpret_cast<void**>(&runtimeInfo));
	if(FAILED(result))
		throw result;

	BOOL isLoaded = FALSE;
	result = runtimeInfo->IsLoaded(GetCurrentProcess(), &isLoaded);
	if(FAILED(result))
		throw result;

	result = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, reinterpret_cast<void**>(&gRuntimeHost));
	if(FAILED(result))
		throw result;

	if(isLoaded)
	{
		MessageBox(0, L"returning", 0,0);
		return;
	}

	result = gRuntimeHost->Start();
	if(FAILED(result))
		throw result;
}


void Execute(const StartupInfo& info)
{
	HRESULT result;

	result = gRuntimeHost->ExecuteInDefaultAppDomain(info.assemblyPath.c_str(), 
													 info.typeName.c_str(), 
													 info.functionName.c_str(), 
													 info.parameters.c_str(), 
													 &gAppDomain);
	if(FAILED(result))
		throw result;
}


extern "C" __declspec(dllexport) long Start()
{
	try
	{
		auto startupInfo = ExtractStartupInfo();
		LoadCLR();
		Execute(startupInfo);
	}
	catch(HRESULT hr)
	{
		_com_error error(hr);
		MessageBox(nullptr, error.ErrorMessage(), error.Description(), MB_OK | MB_ICONERROR);
	}
	catch(int i)
	{
		if(i == 0)
			MessageBox(nullptr, _T("WAAAAAH"), _T("WAAAAH"), MB_OK | MB_ICONERROR);
		else if(i == 1)
			MessageBox(nullptr, _T("WTFBBQ"), _T("WTFBBQ"), MB_OK | MB_ICONERROR);
	}
	catch(...)
	{
	}

	return 0;
}


extern "C" __declspec(dllexport) void End()
{
	MessageBoxA(0, "Bye!",0,0);

	if(gRuntimeHost)
	{
		/*gRuntimeHost->GetCurrentAppDomainId(&gAppDomain);
		gRuntimeHost->UnloadAppDomain(gAppDomain, TRUE);
		gRuntimeHost->Stop();*/
	}
}